package cn.spdb.harrier.common.dynamicjava;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class DynamicClassManager {

	private static class DynamicClassManagerIner{
		public static DynamicClassManager dynamicClassManager=new DynamicClassManager();
	}
	
	public static DynamicClassManager getInstance() {
		return DynamicClassManagerIner.dynamicClassManager;
	}
	
	private ConcurrentHashMap<String,DynamicClassLoader> HASH_MAP=new ConcurrentHashMap<String,DynamicClassLoader>();

	private final String classPath;

	private DynamicClassManager() {
		URLClassLoader classLoader = (URLClassLoader)this.getClass().getClassLoader();
		StringBuilder sBuilder = new StringBuilder();
		for (URL url : classLoader.getURLs()) {
			String pString = url.getFile();
			if (pString.startsWith("/")) {
				pString = pString.substring(1);
			}
			sBuilder.append(pString).append(File.pathSeparator);
		}
		this.classPath = sBuilder.toString();
	}

	public Class<?> loadFromJavaFile(File file) throws IOException {
		try (FileInputStream inputStream = new FileInputStream(file);) {
			byte[] bytes = new byte[(int) file.length()];
			inputStream.read(bytes);
			String javaCode = new String(bytes);
			return javaCodeToObject(file.toURI(), javaCode);
		} catch (IOException e) {
			e.printStackTrace();
			throw e;
		}
	}

/**
 * 将 Java 源代码字符串动态编译为 Class 对象。
 *
 * <p>该方法使用 JDK 提供的 {@link JavaCompiler} 工具将 Java 源码进行编译，
 * 并通过自定义类加载器将其加载为可使用的 Class 对象。
 *
 * <h3>主要流程如下：</h3>
 * <ol>
 *   <li>获取系统 Java 编译器</li>
 *   <li>设置编译参数（编码、classpath）</li>
 *   <li>将源码封装为 JavaFileObject 并提交编译任务</li>
 *   <li>如果编译成功，通过 DynamicClassLoader 加载主类和内部类</li>
 *   <li>返回编译后的 Class 对象</li>
 * </ol>
 *
 * @param uri       Java 类对应的 URI（通常是文件路径）
 * @param javaCode  Java 源代码字符串
 * @return 成功编译并加载的 Class 对象，失败返回 null
 */
public Class<?> javaCodeToObject(URI uri, String javaCode) {
    long start = System.currentTimeMillis(); // 记录开始时间用于性能统计

    // 获取系统自带的 Java 编译器（需要 JDK 环境支持）
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

    // 创建诊断收集器，用于捕获编译过程中的警告或错误信息
    DiagnosticCollector<JavaFileObject> diagnosticsr = new DiagnosticCollector<>();

    // 获取标准 Java 文件管理器，用于处理输入输出文件对象
    StandardJavaFileManager standardJavaFileManager = compiler.getStandardFileManager(diagnosticsr, null, null);

    // 使用自定义文件管理器 ClassFileManager，用于保存编译生成的字节码
    ClassFileManager fileManager = new ClassFileManager(standardJavaFileManager);

    // 构建 Java 源码对象列表（可以添加多个类，这里只添加一个）
    List<JavaFileObject> fileObjects = new ArrayList<>();
    JavaFileObject javaFileObject = CharSequenceJavaFileObject.build(uri, javaCode);
    fileObjects.add(javaFileObject);

    // 设置编译选项：编码格式 UTF-8，以及 classpath（依赖库路径）
    List<String> options = new ArrayList<>();
    options.add("-encoding");
    options.add("UTF-8");
    options.add("-classpath");
    options.add(this.classPath); // classpath 来自构造函数初始化

    // 创建编译任务
    CompilationTask task = compiler.getTask(null, fileManager, diagnosticsr, options, null, fileObjects);

    // 执行编译任务
    boolean success = task.call();

    // 创建自定义类加载器 DynamicClassLoader，用于加载编译后的字节码
    DynamicClassLoader classLoader = new DynamicClassLoader(this.getClass().getClassLoader());

    if (success) {
        // 获取主类的字节码对象
        JavaClassObject javaClassObject = fileManager.getMainJavaClassObject();

        // 获取所有内部类的字节码对象，并逐一加载
        List<JavaClassObject> innerClassObjects = fileManager.getInnerClassJavaClassObjects();
        if (innerClassObjects != null && !innerClassObjects.isEmpty()) {
            for (JavaClassObject inner : innerClassObjects) {
                classLoader.loadClass(inner); // 加载内部类
            }
        }

        // 加载主类
        Class<?> clazz = classLoader.loadClass(javaClassObject);

        if (clazz != null) {
            // 编译成功，打印耗时日志
            long end = System.currentTimeMillis();
            System.out.println(uri.toString() + "\t javaCodeToObject use :" + (end - start) + " ms");
            return clazz; // 返回编译后的类
        }
    } else {
        // 编译失败，收集所有错误信息并打印
        StringBuffer error = new StringBuffer();
        for (Diagnostic<?> diagnostic : diagnosticsr.getDiagnostics()) {
            error.append(compilePrint(diagnostic)); // 格式化错误信息
        }
        System.err.println("Exception in compile \t" + uri.toString() + "\t " + error);
    }

    return null; // 返回 null 表示编译失败
}

	private String compilePrint(Diagnostic<?> diagnostic) {
		StringBuffer res = new StringBuffer();
		res.append("Code:[" + diagnostic.getCode() + "]\n");
		res.append("Kind:[" + diagnostic.getKind() + "]\n");
		res.append("Position:[" + diagnostic.getPosition() + "]\n");
		res.append("Start Position:[" + diagnostic.getStartPosition() + "]\n");
		res.append("End Position:[" + diagnostic.getEndPosition() + "]\n");
		res.append("Source:[" + diagnostic.getSource() + "]\n");
		res.append("Message:[" + diagnostic.getMessage(null) + "]\n");
		res.append("LineNumber:[" + diagnostic.getLineNumber() + "]\n");
		res.append("ColumnNumber:[" + diagnostic.getColumnNumber() + "]\n");

		return res.toString();
	}

	public Class<?> loadClass(File file) throws IOException {
		DynamicClassLoader classLoader = new DynamicClassLoader(this.getClass().getClassLoader());
		return classLoader.loadClass(file);
	}

}
